QPanda 2教程资源|模拟量子虚拟机的解决方案(上):全振幅和部分振幅量子虚拟机
hi~一周又开始啦
继上篇的推文后
《构建量子程序、QWhile、QIf的方法》
小编在新的一周
马不停蹄地
为大家运输“新能源”
来和我们一起看看吧!
在上一章节里,小编已为大家详细介绍了构建量子程序、QWhile、QIf的方法。量子程序设计研究的是如何为将来的量子计算机设计程序,而在真正的量子计算机未成型前,我们需要使用量子虚拟机承担量子算法、量子应用的验证问题。
QPanda 2目前支持全振幅量子虚拟机、部分振幅量子虚拟机、单振幅量子虚拟机以及含噪声量子虚拟机。小编今天为大家带来的是模拟量子虚拟机的解决方案(上):全振幅和部分振幅量子虚拟机。
全振幅量子虚拟机
全振幅量子虚拟机一次可模拟计算出量子态的所有振幅,计算方式支持CPU、单线程计算和GPU(GPU计算方式只有在用户主机上有英伟达的GPU硬件且安装了CUDA库的时候方可使用),可在初始化时配置。使用方式是完全一样的,只是其计算效率不同。
接口介绍
全振幅量子虚拟机类型:
enum QMachineType {
CPU, /**< Cpu quantum machine */
GPU, /**< Gpu quantum machine */
CPU_SINGLE_THREAD, /**< Cpu quantum machine with single thread */ NOISE /**< Cpu quantum machine with noise */
};
QPanda 2中在构造量子虚拟机时有以下几种方式:
init(QMachineType::CPU); // 使用init,不会返回qvm,会在代码中生成一个全局的qvm
auto qvm = initQuantumMachine(QMachineType::CPU); // 通过接口得到qvm指针
CPUQVM *qvm = new CPUQVM; // 直接new一个需要qvm类
注解
init和initQuantumMachine这两个函数不是线程安全的,不适用于多线程编程,而且其最大的量子比特个数和经典寄存器个数均为默认值25。
设置量子虚拟机的配置(当前配置只有最大量子比特个数和最大经典寄存器个数):
Configuration conf;
conf.maxQubit = 30;
conf.maxCMem = 30;
qvm->setConfig(conf);
注解
量子虚拟机默认的最大量子比特个数和经典寄存器个数均为25。
设置好配置之后要初始化量子虚拟机:
qvm->init();
注解
调用init和initQuantumMachine接口, 就不需要初始化了。
下面我们就需要去申请量子比特和经典寄存器。
例如我们申请4个量子比特:
QVec qubits = qvm->allocateQubits(4);
申请一个量子比特时也可以用这个接口:
Qubit* qubit = qvm->allocateQubit();
如果我们想在固定的量子比特虚拟地址上申请一个量子比特可以用下面的方法:
Qubit* qubit = qvm->allocateQubitThroughVirAddress(0x01);
申请经典寄存器也有类似于申请量子比特的接口,其使用方法和申请量子比特的方法一样,如申请4个经典寄存器的方法:
std::vector<ClassicalCondition> cbits = qvm->allocateCBits(4);
申请一个经典寄存器时也可以用这个接口:
ClassicalCondition cbit = qvm->allocateCBit();
固定的经典寄存器虚拟地址上申请一个量子比特可以用下面的方法:
ClassicalCondition cbit = qvm->allocateCBit(0x01);
在一个量子虚拟机中,申请了几次量子比特或经典寄存器,我们想知道一共申请了多少个量子比特或经典寄存器可以用下面的方法:
size_t num_qubit = qvm->getAllocateQubit(); // 申请量子比特的个数
size_t num_cbit = qvm->getAllocateCMem(); // 申请经典寄存器的个数
我们该如何使用量子虚拟机来执行量子程序呢?可以用下面方法:
QProg prog;
prog << H(qubits[0])
<< CNOT(qubits[0], qubits[1])
<< Measure(qubits[0], cbits[0]); // 构建一个量子程序
map<string, bool> result = qvm->directlyRun(prog); // 执行量子程序
如果想多次运行一个量子程序,并得到每次量子程序的结果,除了循环调用directlyRun方法外,我们还提供了一个接口runWithConfiguration。为了以后的扩展,runWithConfiguration配置参数是一个rapidjson::Document类型的参数, rapidjson::Document保存的是一个Json对象,由于现在只支持量子程序运行次数的配置,其json数据结构为:
{
"shots":1000}
利用rapidjson库去得到rapidjson::Document对象, rapidjson的使用可以参照Rapidjson首页。举个例子如下:
int shots = 1000;
rapidjson::Document doc;
doc.Parse("{}");
doc.AddMember("shots",
shots,
doc.GetAllocator());qvm->runWithConfiguration(prog, cbits, doc);
如果想得到量子程序运行之后各个量子态的振幅值,可以调用getQState函数获得:
QStat stat = qvm->getQState();
量子虚拟机中测量和概率使用方法与量子测量和概率测量中介绍的相同,在这里就不多做赘述。
QPanda2中还提供了一些面向过程的接口,其使用方法和面向对象的方式相似,下面提供一份面向对象与面向过程的函数对照表:
QPanda2中设计了PartialAmplitudeQVM类用于运行部分振幅模拟量子计算,同时提供了相关接口,它的使用很简单。
首先,构建一个部分振幅量子虚拟机;
auto machine = new PartialAmplitudeQVM();
然后,必须使用PartialAmplitudeQVM::init()初始化量子虚拟机环境;
machine->init();
接着,进行量子程序的构建、装载工作;
auto prog = QProg();
auto qlist = machine->allocateQubits(10);
for_each(qlist.begin(), qlist.end(), [&](Qubit *val) { prog << H(val); });
prog << CZ(qlist[1], qlist[5]) << CZ(qlist[3], qlist[5]) << CZ(qlist[2], qlist[4]);
...
machine->run(prog);
构建还可以采用另一种方式,即读取QRunes文件形式。例如:
machine->run("D:\\QRunes");
最后,调用计算接口,我们设计多种返回值的接口用于满足不同的计算需求。具体见示例所述:
以下实例,主要展现了部分振幅量子虚拟机接口的使用方式。
1、初始化虚拟机环境:构建一个部分振幅量子虚拟机,向量子虚拟机申请10个量子比特;
2、构建量子程序:构建一个量子线路prog,通过 << 操作符把量子逻辑门插入到prog中;
3、运行量子程序:调用run装载量子程序,调用getQStat接口得到获取量子态所有分量的振幅。
上述程序使用的接口为getQStat(),即获取量子态所有分量的振幅,计算结果如下:
(-0.00647209,-0.00647209)
(8.5444e-18,-0.00915291)
...
若使用其他接口:
PMeasure(std::string) ,输入的参数表示获取测量所有比特构成量子态的概率的结果集的前多少项,比如如下例子,我们获取所有量子态的概率分布结果的前6项,程序运行如下:
auto res = machine->PMeasure("6");
for (auto val :res)
{
std::cout << val.first << " : " << val.second << std::endl;
}
结果输出如下,每个结果的序号表示量子态的下标,后面的值表示概率:
0 : 8.37758e-05
1 : 8.37758e-05
2 : 8.37758e-05
3 : 8.37758e-05
4 : 0.000488281
5 : 0.000488281
PMeasure(QVec,std::string) ,输入的第一个参数表示选取哪几个量子比特构成的量子态的概率,第二个参数表示选取结果的前多少项。使用示例如下:
QVec qv = { qlist[1],qlist[2],qlist[3] ,qlist[4] ,qlist[5] ,qlist[6] ,qlist[7] ,qlist[8],qlist[9] };
auto res2 = machine->PMeasure(qv, "6");
for (auto val :res)
{
std::cout << val.first << " : " << val.second << std::endl;
}
结果输出如下,每个结果的序号表示量子态的下标,后面的值表示概率:
0 : 0.000167552
1 : 0.000167552
2 : 0.000976562
3 : 0.000976562
4 : 0.000976562
5 : 0.000976562
getProbDict(qvec,std::string) ,输入的第一个参数表示选取哪几个量子比特构成的量子态的概率,第二个参数表示选取结果的前多少项,使用示例如下:
QVec qvec;
for_each(qlist.begin(), qlist.end(), [&](Qubit *val) { qvec.emplace_back(val); });
auto res = machine->getProbDict(qvec,6);
for (auto val :res)
{
std::cout << val.first << " : " << val.second << endl;
}
结果输出如下,每个结果的前半部分表示量子态的二进制形式,后面的值表示概率:
0000000000 : 8.37758e-05
0000000001 : 8.37758e-05
0000000010 : 8.37758e-05
0000000011 : 8.37758e-05
0000000100 : 0.000488281
0000000101 : 0.000488281
PMeasure_bin_index(std::string) ,输入的参数表示指定需要测量的量子态二进制形式,使用示例如下:
auto res = machine->PMeasure_bin_index("0000000001");
std::cout << res << std::endl;
结果输出如下,表示目标量子态的概率值:
8.37758e-05
PMeasure_dec_index(std::string) ,输入的参数表示指定需要测量的量子态十进制下标形式,使用示例
auto res = machine->PMeasure_bin_index("1");
std::cout << res << std::endl;
结果输出如下,表示目标量子态的概率值:
8.37758e-05
PMeasureSubSet(QProg &, std::vector<std::string>),输入的第一个参数表示待运行的量子线路,第二个参数表示需要测量的量子态二进制下标形式构成的子集,使用示例如下:
std::vector<std::string> set = { "0000000000","0000000001","0000000100" };
auto res = machine->PMeasureSubSet(prog, set);
for (auto val : res)
{
std::cout << val.first << " : " << val.second << endl;
}
结果输出如下:
0000000000 : 8.37758e-05
0000000001 : 8.37758e-05
0000000100 : 0.000488281
★以上即为QPanda 2全振幅和部分振幅量子虚拟机部分的详细内容介绍。
★感兴趣的欢迎加入"QPanda 2开发交流群"。(关注“本源量子”公众号,回复“加群”,联系小编即可)
★PC端学习量子计算请登录learn-quantum.com
★掌上学习请下载"本源溯知APP"
★如有疑问可上量子互动论坛,与我们的大神进行交流讨论。
往期精彩回顾
QPanda 2教程资源 | 构建量子程序、QWhile与QIf的方法
【新品】国内首款量子软件开发包QPanda 2.0 升级发布!